﻿using Common;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace MVVM
{
    /// <summary>
    /// 属性更改通知
    /// </summary>
    public class NotifyPropertyBase : DynamicObject, INotifyPropertyChanged
    {
        /// <summary>
        /// 属性更改通知
        /// </summary> 
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 存储动态属性值
        /// </summary>
        private IDictionary<string, object> _dictionary { get; set; } = new ConcurrentDictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        /// <summary>
        /// 属性修改
        /// </summary>
        /// <param name="propertyName"> </param>
        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public NotifyPropertyBase()
            : base()
        {
        }

        /// <summary>
        /// 属性设置
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            this.SetValue(value, binder.Name);
            return base.TrySetMember(binder, value);
        }

        /// <summary>
        /// 属性设置
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="value"> </param>
        /// <param name="propertyName"> </param>
        public void SetValue<T>(T value, [CallerMemberName] string propertyName = "")
        {
            this._dictionary[propertyName] = value;
            OnPropertyChanged(propertyName);
        }

        /// <summary>
        /// 属性获取
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            if (base.TryGetMember(binder, out result))
            {
                return true;
            }

            result = this.GetValue<object>(binder.Name);// ? this._dictionary[binder.Name] : null;
            return result != null;
        }

        /// <summary>
        /// 属性获取
        /// </summary>
        /// <typeparam name="T"> </typeparam>
        /// <param name="propertyName"> </param>
        /// <returns> </returns>
        public virtual T GetValue<T>([CallerMemberName] string propertyName = "")
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                $"属性获取时名称为空！".Warn();
                return default(T);
            }

            //获取属性
            object _propertyValue = this._dictionary.ContainsKey(propertyName) ? this._dictionary[propertyName] : null;
            if (_propertyValue != null)
            {
                return (T)_propertyValue;
            }

            //获取属性
            var property = this.GetType().GetProperty(propertyName);
            if (property == null)
            {
                return default(T);
            }
            //获取属性上的特性类
            var attributes = property.GetCustomAttributes(false);
            if (attributes == null || attributes.Length <= 0)
            {
                return default(T);
            }

            //构造特性
            var attribute = attributes.FirstOrDefault(o => o is AutoConstructionAttribute);
            if (attribute == null)
            {
                if (_propertyValue != null)
                {
                    return (T)_propertyValue;
                }
                else
                {
                    return default(T);
                }
            }

            AutoConstructionAttribute autoConstructionAttribute = attribute as AutoConstructionAttribute;
            //获取属性构造类型
            Type objType = autoConstructionAttribute.Type;
            if (objType == null)
            {
                objType = typeof(T);
            }

            if (autoConstructionAttribute.IsSingle)//单例
            {
                lock (AutoConstructionAttribute.ValueDictionary)
                {
                    if (AutoConstructionAttribute.ValueDictionary.TryGetValue(objType, out _propertyValue) && _propertyValue != null)
                    {
                        this.SetValue(_propertyValue, propertyName);
                        return (T)_propertyValue;
                    }
                }
            }

            //获取属性构造时使用的参数
            object args = autoConstructionAttribute.args;
            try
            {
                if (args == null)//自动构造
                {
                    _propertyValue = Activator.CreateInstance(objType);
                }
                else
                {
                    _propertyValue = Activator.CreateInstance(objType, args);
                }
                this.SetValue(_propertyValue, propertyName);
                if (autoConstructionAttribute.IsSingle)//单例
                {
                    lock (AutoConstructionAttribute.ValueDictionary)
                    {
                        AutoConstructionAttribute.ValueDictionary[objType] = _propertyValue;
                    }
                }
            }
            catch (Exception ex)
            {
                "属性获取异常：".Debug(ex);
            }

            return (T)_propertyValue;
        }
    }
}